#include "bsp_rtc.h"


/* BSP-RTC */


#define RTC_CFG_LSI    0x5050      // 内部低速震荡
#define RTC_CFG_LSE    0x5051      // 外部低速震荡
#define RTC_CFG_REG    RTC_BKP_DR0

/* static var */
static RTC_HandleTypeDef rtc_handle;


/* static func */

/**
 * @brief 判断是否是闰年函数
 *        月份   1  2  3  4  5  6  7  8  9  10 11 12
 *        闰年   31 29 31 30 31 30 31 31 30 31 30 31
 *        非闰年 31 28 31 30 31 30 31 31 30 31 30 31
 * 
 * @param year 年份(00-99)
 * @return uint8_t 
 */
static uint8_t bsp_rtc_is_leap_year(uint16_t year)
{
    year += 2000;

	if(year%4==0) //必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除 	   
			else return 0;   
		}else return 1;   
	}else return 0;	
}

uint8_t bsp_rtc_get_max_date(uint16_t year, uint8_t month)
{ 
	uint8_t md = 0;
	switch(month)
	{
		case 2:
			if(bsp_rtc_is_leap_year(year)) md=29;
			else md=28;
			break;
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			md=31;
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			md=30;
			break;
	}
	return(md);
}

/**
 * @brief 获得现在是星期几
 * 
 * @param year 年 (00-99)
 * @param month 月
 * @param date 日
 * @return uint8_t 星期号(0~6,代表周日~周六)
 */
static uint8_t bsp_rtc_get_day(uint8_t year, uint8_t month, uint8_t date)
{
    const uint8_t table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; // 月修正数据表

    uint16_t temp2;

    temp2 = year + year / 4;
    temp2 = temp2 % 7;
    temp2 = temp2 + date + table_week[month - 1];
    if (year % 4 == 0 && month < 3) temp2--;
    temp2 %= 7;

    return temp2;
}

static uint8_t bsp_rtc_time_check_lowful(_bsp_rtc_time_t *obj)
{
    uint8_t ret = 0;
    uint8_t tmp;

    if (obj->year > 99)
    {
        obj->year = 0;
        ret |= (1<<0);
    }

    if (obj->month == 0 || obj->month > 12)
    {
        obj->month = 1;
        ret |= (1<<1);
    }

    tmp = bsp_rtc_get_max_date(obj->year, obj->month);
    if (obj->date == 0 || obj->date > tmp)
    {
        obj->date = 1;
        ret |= (1<<2);
    }

    if (obj->hour > 23)
    {
        obj->hour = 0;
        ret |= (1<<3);
    }

    if (obj->min > 59)
    {
        obj->min = 0;
        ret |= (1<<4);
    }

    if (obj->sec > 59)
    {
        obj->sec = 0;
        ret |= (1<<5);
    }

    return ret;
}


/* export func */

/**
 * @brief 初始化
 * 
 */
void bsp_rtc_init(void)
{
    uint16_t flag = 0;

    __HAL_RCC_RTC_CLK_ENABLE();
    __HAL_RCC_RTC_ENABLE();
    // HAL_PWR_EnableBkUpAccess();     // 取消备份区写保护

    /* 时钟配置移到 bsp_clock.c */
    // {
    //     RCC_OscInitTypeDef RCC_OscInitStruct;
    //     RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
    //     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    //     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    //     RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    //     RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
    //     HAL_RCC_OscConfig(&RCC_OscInitStruct);
    //     PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    //     PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    //     HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
    // }

    rtc_handle.Instance = RTC;
    rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;
    rtc_handle.Init.AsynchPrediv = 0x7F;    // RTC异步分频系数(1~0X7F)
    rtc_handle.Init.SynchPrediv = 0xFF;     // RTC同步分频系数(0~7FFF)
    rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;
    rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    rtc_handle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;

    if (HAL_RTC_Init(&rtc_handle) != HAL_OK)
    {
        printf("rtc init fail\r\n");
    }

    flag = bsp_rtc_bkr_read(RTC_CFG_REG);
    // printf("rtc flag: 0x%04X\r\n", flag);
    if (flag != RTC_CFG_LSE)
    {
        _bsp_rtc_time_t obj;

        obj.year = 22;
        obj.month = 1;
        obj.date = 1;
        obj.hour = 8;
        obj.min = 0;
        obj.sec = 0;
        bsp_rtc_set_time(&obj);     // 设置默认时间
        bsp_rtc_bkr_write(RTC_CFG_REG, RTC_CFG_LSE);    // 初始化标志
        printf("bsp rtc first init\r\n");
    }

}

/**
 * @brief 获取rtc时间
 * 
 * @param obj 
 * @return int 
 */
int bsp_rtc_get_time(_bsp_rtc_time_t *obj)
{
    RTC_DateTypeDef RTC_DateStructure;
    RTC_TimeTypeDef RTC_TimeStructure;
    uint8_t ret;
    
    // 使用STM32H7的RTC依然要先读时间，再读日期，这个老毛病在F4上就有
    HAL_RTC_GetTime(&rtc_handle, &RTC_TimeStructure, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&rtc_handle, &RTC_DateStructure, RTC_FORMAT_BIN);

    obj->year = RTC_DateStructure.Year;
    obj->month = RTC_DateStructure.Month;
    obj->date = RTC_DateStructure.Date;
    obj->hour = RTC_TimeStructure.Hours;
    obj->min = RTC_TimeStructure.Minutes;
    obj->sec = RTC_TimeStructure.Seconds;
    ret = bsp_rtc_time_check_lowful(obj);

    obj->day = bsp_rtc_get_day(obj->year, obj->month, obj->date);

    return ret;
}

/**
 * @brief 设置rtc时间
 * 
 * @param obj 
 * @return int 
 */
int bsp_rtc_set_time(_bsp_rtc_time_t *obj)
{
    RTC_DateTypeDef RTC_DateStructure;
    RTC_TimeTypeDef RTC_TimeStructure;
    uint8_t tmp;

    bsp_rtc_time_check_lowful(obj);
    RTC_DateStructure.Year = obj->year;
    RTC_DateStructure.Month = obj->month;
    RTC_DateStructure.Date = obj->date;
    obj->day = bsp_rtc_get_day(obj->year, obj->month, obj->date);
    switch (obj->day)
    {
    case 0: tmp = RTC_WEEKDAY_SUNDAY; break;
    case 1: tmp = RTC_WEEKDAY_MONDAY; break;
    case 2: tmp = RTC_WEEKDAY_TUESDAY; break;
    case 3: tmp = RTC_WEEKDAY_WEDNESDAY; break;
    case 4: tmp = RTC_WEEKDAY_THURSDAY; break;
    case 5: tmp = RTC_WEEKDAY_FRIDAY; break;
    case 6: tmp = RTC_WEEKDAY_SATURDAY; break;
    default: tmp = RTC_WEEKDAY_SATURDAY; break;
    }
    RTC_DateStructure.WeekDay = tmp;

    RTC_TimeStructure.Hours = obj->hour;
    RTC_TimeStructure.Minutes = obj->min;
    RTC_TimeStructure.Seconds = obj->sec;

    RTC_TimeStructure.TimeFormat = RTC_FORMAT_BIN;
    RTC_TimeStructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    RTC_TimeStructure.StoreOperation = RTC_STOREOPERATION_RESET;

    HAL_RTC_SetTime(&rtc_handle, &RTC_TimeStructure, RTC_FORMAT_BIN);
    HAL_RTC_SetDate(&rtc_handle, &RTC_DateStructure, RTC_FORMAT_BIN);

    return 0;
}

/**
 * @brief RTC备份区读取
 * 
 * @param reg 备份区寄存器编号，范围0-31
 * @return uint32_t 
 */
uint32_t bsp_rtc_bkr_read(uint32_t reg)
{
    uint32_t data;
    data = HAL_RTCEx_BKUPRead(&rtc_handle, reg);
    return data;
}

/**
 * @brief RTC备份区写入
 * 
 * @param reg 备份区寄存器编号，范围0-31
 * @param data 
 */
void bsp_rtc_bkr_write(uint32_t reg, uint32_t data)
{
    HAL_PWR_EnableBkUpAccess();
    HAL_RTCEx_BKUPWrite(&rtc_handle, reg, data);
    HAL_PWR_DisableBkUpAccess();
}
